import * as ws from "ws";
import * as http from "http";

import { AService, ServiceType } from "./AService";
import { WChannel } from "./WChannel";
import { NetServices } from "./NetServices";
import { Log } from "../Log/Log";
import { Dictionary } from "../../../Support/Share/Lib/Dictionary";
import { WebSocketExtentions } from "../../../Support/Share/WebSocket/WebSocketExtentions";
import { MacroDefine } from "../../../Support/Share/Global/MacroDefine";

export class WService extends AService {

    private idGenerater: number = 200000000;

    private httpListener: http.Server;

    private acceptor: ws.WebSocketServer;

    private readonly channels = new Dictionary<number, WChannel>();

    constructor(ipEndPoint: string = "", serviceType: ServiceType) {
        super();

        this.ServiceType = serviceType;

        if (ipEndPoint === "" || ipEndPoint === undefined) {
            return;
        }

        const param = ipEndPoint.split(":");
        const port = param[1];

        // console.log(`WService listen to ${ipEndPoint}`);
        // console.log(param);

        this.httpListener = http.createServer();

        this.acceptor = new ws.Server({ server: this.httpListener });

        this.acceptor.on("connection", this.onConnection.bind(this));

        this.httpListener.listen(port, 1000);
    }

    private get GetId(): number {
        return ++this.idGenerater;
    }

    private async onConnection(acceptSocket: ws.WebSocket) {
        if (this.acceptor == null) {
            return;
        }
        try {
            let channel = new WChannel(this.GetId, acceptSocket, this);
            await channel.initWebSocket();
            this.channels.Add(channel.Id, channel);
            let channelId = channel.Id;

            // console.log(`WService onConnection ${channelId}`);

            NetServices.Instance.OnAccept(this.Id, channelId, channel.RemoteAddress);
        } catch (e) {
            Log.Error(e);
        }
    }

    public Update() {
    }

    public Remove(id: number, error: number = 0) {
        let channel = this.channels.TryGetValue(id);
        if (!channel) {
            return;
        }
        channel.Error = error;
        this.channels.Remove(id);
        channel.Dispose();
    }

    public IsDispose() {
    }

    public async Create(id: number, address: string) {
        if (MacroDefine.ET_SERVER) {
            // console.log(`WService.Create Server WebSocket`);
            // console.log(address);
            let webSocket = new ws.WebSocket(address);
            let channel = new WChannel(id, webSocket, this);
            await channel.initServerWebSocket();
            this.channels.Add(channel.Id, channel);
            return channel;
        } else {
            let protocols = WebSocketExtentions.GetProtocols();
            let url = WebSocketExtentions.GetCAUrl();
            // @ts-ignore
            let webSocket = new WebSocket(address, protocols, url);
            let channel = new WChannel(id, webSocket, this);
            await channel.initClientWebSocket();
            this.channels.Add(channel.Id, channel);
            return channel;
        }
    }

    public Send(channelId: number, actorId: number, message: any) {
        let channel = this.channels.TryGetValue(channelId);
        if (channel == null) {
            return;
        }
        let memoryStream = this.GetMemoryStream(message);
        channel.Send(actorId, memoryStream.buffer as Buffer);
    }

    public Dispose() {
        super.Dispose();

        this.acceptor?.close();
        this.acceptor = null;

        this.httpListener?.close();
        this.httpListener = null;
    }

}